﻿using System.Collections.Concurrent;
using System.Diagnostics;

namespace UPKAndUDKUnpackTools.Tools;

/// <summary>
///     处理 UDK 文件的类。
/// </summary>
public static class UdkProcessor
{
    private static readonly ParallelOptions Options;

    static UdkProcessor()
    {
        Options = new ParallelOptions { MaxDegreeOfParallelism = 3 };
    }

    public static event Action<int> OnProgressUpdate;

    /// <summary>
    ///     处理解锁的 UDK 文件。
    /// </summary>
    public static void ProcessFiles()
    {
        var unpackedDir = Path.Combine(Directory.GetCurrentDirectory(), "unpacked");
        var filePaths = Directory.GetFiles(unpackedDir, "*.udk", SearchOption.AllDirectories);
    
        foreach (var filePath in filePaths)
        {
            UnlockUdkFile(filePath);
        }
    
        OnProgressUpdate?.Invoke(100);
    }


    private static void UnlockUdkFile(string filename)
    {
        var outputDir = Path.Combine(Directory.GetCurrentDirectory(), "unlockedUdk");
        Directory.CreateDirectory(outputDir);
        var newFilename = Path.Combine(outputDir, Path.GetFileNameWithoutExtension(filename) + "_unpacked.udk");

        if (File.Exists(newFilename))
        {
            Console.WriteLine($"{filename} 文件已解锁，跳过处理。");
            return;
        }

        byte[] targetSequence = [0x00, 0x00, 0x00, 0x00, 0x01, 0x00];
        var data = File.ReadAllBytes(filename);
        var position = FindSequence(data, targetSequence);

        if (position == -1)
        {
            Console.WriteLine("未找到序列。");
            return;
        }

        var index12 = position + 12;
        var index15 = position + 15;
        if (index12 >= data.Length || index15 >= data.Length)
        {
            Console.WriteLine("索引 20 或 23 超出范围。");
            return;
        }

        var swappedValue = BitConverter.ToInt32(data, index12);
        if (swappedValue >= data.Length)
        {
            Console.WriteLine("swappedValue 超出范围。");
            return;
        }

        var bytesToDelete = GetBytesToDelete(data, swappedValue);
        if (swappedValue + bytesToDelete > data.Length)
        {
            Console.WriteLine("swappedValue + bytesToDelete 超出范围。");
            return;
        }

        Console.WriteLine($"文件：{GetSubstringFromRight(filename)} 偏移量为：{swappedValue:X8}，删除：{bytesToDelete} 个00字节。");

        var newData = new byte[data.Length - bytesToDelete];
        Array.Copy(data, 0, newData, 0, swappedValue);
        Array.Copy(data, swappedValue + bytesToDelete, newData, swappedValue,
            data.Length - (swappedValue + bytesToDelete));

        File.WriteAllBytes(newFilename, newData);
    }

    public static void ProcessUdkFiles(string inputDir, string outputDir)
    {
        Directory.CreateDirectory(outputDir);
        var filePaths = Directory.GetFiles(inputDir, "*.udk", SearchOption.AllDirectories);

        var modifiedFilePaths = new ConcurrentBag<string>();
        OnProgressUpdate?.Invoke(25);
        Parallel.ForEach(filePaths, Options, filePath =>
        {
            var fileName = Path.GetFileName(filePath);

            // 跳过包含 "light" 的文件名
            if (fileName.Contains("light", StringComparison.OrdinalIgnoreCase))
            {
                Console.WriteLine($"跳过文件: {GetSubstringFromRight(fileName)} (包含 'light')");
                return;
            }

            Console.WriteLine($"正在修改 {GetSubstringFromRight(filePath)}");
            var modifiedFileName = fileName.Replace(".udk", "_modified_unpacked.udk");

            var modifiedFilePath = Path.Combine(outputDir, modifiedFileName);

            if (File.Exists(modifiedFilePath))
            {
                Console.WriteLine("已解锁的文件已存在，跳过操作: " + GetSubstringFromRight(fileName));
                return;
            }

            modifiedFilePath = ModifyUdkFile(filePath, outputDir);
            modifiedFilePaths.Add(modifiedFilePath);
            Console.WriteLine($"完成修改 {GetSubstringFromRight(filePath)}");
        });

        OnProgressUpdate?.Invoke(75);
        foreach (var modifiedFilePath in modifiedFilePaths) RunExternalPrograms(modifiedFilePath);
    }


    private static string ModifyUdkFile(string filePath, string outputDir)
    {
        var data = File.ReadAllBytes(filePath);
        data[0] = 0xFF;
        data[1] = 0xA8;
        data[2] = 0x27;
        data[3] = 0x3E;
        data[4] = (byte)((data[4] + 8) % 256);
        var fileName = Path.GetFileName(filePath);
        var modifiedFilePath = Path.Combine(outputDir, fileName.Replace(".udk", "_modified.udk"));
        File.WriteAllBytes(modifiedFilePath, data);

        return modifiedFilePath;
    }

    private static void RunExternalPrograms(string modifiedFilePath)
    {
        Console.ForegroundColor = ConsoleColor.Yellow;
        Console.WriteLine($"使用 UnPack.exe 处理 {GetSubstringFromRight(modifiedFilePath)}");
        Console.ResetColor();
        var startInfo = new ProcessStartInfo
        {
            FileName = "UnPack.exe",
            Arguments = modifiedFilePath,
            UseShellExecute = false,
            CreateNoWindow = true,
            RedirectStandardOutput = true
        };
        Process.Start(startInfo)?.WaitForExit();
        const string tempDir = "ucpUdk";
        Directory.CreateDirectory(tempDir);
        var currentDir = Directory.GetCurrentDirectory();
        foreach (var file in Directory.GetFiles(currentDir, "*_ucp.upk"))
        {
            var ucpFilePath = Path.Combine(currentDir, file);
            var destinationPath = Path.Combine(tempDir, Path.GetFileName(file));
            File.Move(ucpFilePath, destinationPath);
            var absoluteUcpPath = Path.GetFullPath(destinationPath);
            Console.WriteLine($"使用 Repair.exe 修复 {GetSubstringFromRight(modifiedFilePath)}");
            RunRepair(modifiedFilePath, absoluteUcpPath);
            using (var fs = new FileStream(modifiedFilePath, FileMode.Open, FileAccess.Write))
            {
                fs.Seek(0x04, SeekOrigin.Begin);
                fs.Write([0x2C, 0x03], 0, 2);
            }

            Console.ForegroundColor = ConsoleColor.Green;
            Console.WriteLine($"使用 decompress.exe 解压 {GetSubstringFromRight(modifiedFilePath)}");
            Console.ResetColor();
            var dec = new ProcessStartInfo
            {
                FileName = "decompress.exe",
                Arguments = modifiedFilePath,
                UseShellExecute = false,
                CreateNoWindow = true,
                RedirectStandardOutput = true
            };
            Process.Start(dec)?.WaitForExit();
        }
    }

    private static void RunRepair(string filePath, string ucpPath)
    {
        var startInfo = new ProcessStartInfo
        {
            FileName = "Repair.exe",
            Arguments = filePath,
            UseShellExecute = false,
            RedirectStandardInput = true,
            CreateNoWindow = true
        };

        using var process = Process.Start(startInfo);

        if (process != null)
        {
            using (var writer = process.StandardInput)
            {
                if (writer.BaseStream.CanWrite)
                {
                    writer.WriteLine("0");
                    writer.WriteLine(ucpPath);
                    writer.WriteLine("125");
                    writer.WriteLine();
                }
            }

            process.WaitForExit(60000);
        }
        else
        {
            Console.ForegroundColor = ConsoleColor.Red;
            Console.WriteLine("无法启动 Repair.exe。");
            Console.ResetColor();
        }
    }

    /// <summary>
    ///     计算需要删除的字节数
    /// </summary>
    /// <param name="data">数据数组</param>
    /// <param name="swappedValue">交换值，用于计算开始检查的位置</param>
    /// <returns>需要删除的字节数</returns>
    private static int GetBytesToDelete(byte[] data, int swappedValue)
    {
        var count = 1;
        const int bytesToDelete = 28;
        for (var i = swappedValue + 1; i <= swappedValue + bytesToDelete && i < data.Length; i++)
        {
            if (data[i] != 0x00) return count - 4;
            count++;
        }

        return bytesToDelete - 4;
    }


    private static int FindSequence(byte[] data, byte[] sequence)
    {
        // 获取目标字节序列的长度
        var sequenceLength = sequence.Length;
        // 计算数据数组中可以开始查找的最大位置
        var maxPosition = data.Length - sequenceLength;

        // 遍历数据数组，查找目标序列
        for (var i = 0; i <= maxPosition; i++)
        {
            // 假设当前为匹配状态
            var isMatch = true;

            // 深度比较数据数组和目标序列
            for (var j = 0; j < sequenceLength; j++)
            {
                // 如果发现不匹配的字节，则结束当前匹配尝试
                if (data[i + j] == sequence[j]) continue;
                isMatch = false;
                break;
            }

            // 如果未匹配成功，则继续下一次尝试
            if (!isMatch) continue;

            // 如果序列之后是数组边界或紧接着的下一个字节不是0x00，则返回当前位置
            if (i + sequenceLength >= data.Length || data[i + sequenceLength] != 0x00) return i;

            // 假设后续字节全部为零
            var allZero = true;
            // 检查序列之后的四个字节是否全为0x00
            for (var k = 1; k <= 4; k++)
            {
                // 如果发现不满足条件，则结束检查
                if (i + sequenceLength + k < data.Length && data[i + sequenceLength + k] == 0x00) continue;
                allZero = false;
                break;
            }

            // 如果后续字节全为零，则继续下一次查找尝试
            if (allZero)
                continue;

            // 找到符合条件的序列，返回当前位置
            return i;
        }

        // 未找到目标序列，返回-1
        return -1;
    }

    public static void ErrFileHandle(string filename)
    {
        byte[] targetSequence = [0x00, 0x00, 0x00, 0x00, 0x01, 0x00];
        var data = File.ReadAllBytes(filename);
        var position = FindSequence(data, targetSequence);
        if (position == -1)
        {
            Console.WriteLine("未找到序列。");
            return;
        }

        var index35 = position + 12;
        if (index35 >= data.Length)
        {
            Console.WriteLine("索引 19 超出范围。");
            return;
        }

        var swappedValue = BitConverter.ToInt32(data, index35);
        if (swappedValue >= data.Length)
        {
            Console.WriteLine("swappedValue 超出范围。");
            return;
        }

        if (swappedValue + 4 > data.Length)
        {
            Console.WriteLine("swappedValue + 4 超出范围。");
            return;
        }

        Array.Copy(data, swappedValue + 4, data, swappedValue, data.Length - swappedValue - 4);
        Array.Resize(ref data, data.Length - 4);
        File.WriteAllBytes(filename, data);
        Console.WriteLine($"文件已修复: {GetSubstringFromRight(filename)}");
    }

    public static string GetSubstringFromRight(string input)
    {
        if (string.IsNullOrEmpty(input)) return string.Empty;
        // 找到最后一个 '/' 或 '\' 的位置
        var lastIndex = input.LastIndexOfAny(['/', '\\']);

        // 如果没有找到，返回整个字符串
        return lastIndex == -1
            ? input
            :
            // 从最后一个 '/' 或 '\' 之后开始取子字符串
            input[(lastIndex + 1)..];
    }
}